import base64
import json
import re
from fastapi import FastAPI, HTTPException, Body,  UploadFile, File,Query
from fastapi.responses import JSONResponse
from fastapi.encoders import jsonable_encoder
from tortoise import Tortoise,fields
from tortoise.contrib.fastapi import register_tortoise
from tortoise.queryset import QuerySet
from fastapi.middleware.cors import CORSMiddleware
from models.user import User
from models.keyword import Keyword
from models.meal import Meals
from PIL import Image, ImageDraw, ImageFont
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
import io
from random import shuffle
from utils.captcha import *
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.metrics.pairwise import cosine_similarity
from sklearn.preprocessing import normalize
from typing import List,Dict, Set
import ast
from pydantic import BaseModel
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import pairwise_distances

app = FastAPI()

origins = 'http://localhost:5173' 

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# 定义Tortoise ORM模型
# class User(models.Model):
#     id = fields.IntField(pk=True)
#     username = fields.CharField(max_length=255)
#     password = fields.CharField(max_length=255)
#     avatar = fields.CharField(max_length=255)


# 注册Tortoise ORM
register_tortoise(
    app,
    db_url="mysql://root:123456@localhost:3306/fastapi",
    modules={"models": ["main"]},
    generate_schemas=True,
    add_exception_handlers=True,
)


#  -------   用户接口    ----------
# 用户登录
@app.post("/login")
async def login(username=Body(None), password=Body(None)):
    # 查询用户
    user = await User.filter(username=username, password=password).first()
    # 验证用户是否存在
    if user is not None:
        user.lastTime = datetime.now()
        await user.save()
        return JSONResponse(
            status_code=200,
            content={
                "code": 200,
                "status": 200,
                "msg": "登录成功！",
                "data": {
                    "id": user.id,
                    "username": user.username,
                    "avatar": user.avatar,
                    "token": user.token,
                    "role": user.role
                }
            },
            
        )
    # 如果用户不存在或密码错误，抛出HTTP异常
    else:
        raise HTTPException(
            status_code=400,
            detail="账户或密码错误！",
        )


# 注册
@app.put("/signup")
async def createUser(username=Body(None), password=Body(None),role=Body(None)):
    check = await User.filter(username=username).first()
    if check:
        raise HTTPException(
            status_code=400,
            detail="用户名已存在"
        )
    if role == None:
        role = "user"
    current_time = datetime.now()
    formatted_time = current_time.strftime('%Y-%m-%d %H:%M:%S')
    new_user = await User.create(username=username, password=password, time_sign=formatted_time, favorite='[]',token='token',role=role,lastTime=formatted_time,weight_change='[]')
    return JSONResponse(
        status_code=200,
        content={
            "code": 200,
            "status": 200,
            "msg": '注册成功！',

            "data": {
                "id": new_user.id,
                "username": new_user.username,
            }
        }
    )


# 获取验证码
@app.get("/captcha")
async def generate_captcha():
    # 生成随机验证码
    captcha = generate_random_captcha()

    # 创建图片对象
    image = Image.new("RGB", (200, 100), (255, 255, 255))
    draw = ImageDraw.Draw(image)

    # 设置字体
    font = ImageFont.truetype("arial.ttf", size=50)

    # 在图片上绘制验证码
    draw.text((50, 25), captcha, font=font, fill=(0, 0, 0))

    # 将图片转换为 base64 编码
    image_data = io.BytesIO()
    image.save(image_data, format="PNG")
    image_base64 = base64.b64encode(image_data.getvalue()).decode("utf-8")

    # 构建 JSON 响应
    response_data = {
        "captcha": captcha,
        "image_base64": image_base64
    }

    return response_data





# 获取用户信息
@app.get("/getUser/{id}")
async def getUserInfo(id):
    user = await User.filter(id=id).first()
    user_dict = jsonable_encoder(user)  # 将用户对象转换为字典
    return JSONResponse(
        status_code=200,
        content={
            "code": 200,
            "msg": "获取成功",
            "data": user_dict
        }
    )


# 上传头像
@app.post("/upload-image/{user_id}")
async def upload_image(user_id: int, file: UploadFile = File(...)):
    image_data = await file.read()
    user = await User.get_or_none(id=user_id)
    print(user)
    if not user:
        raise HTTPException(status_code=404, msg="User not found")
    base64_data = base64.b64encode(image_data).decode('utf-8')
    user.avatar = base64_data
    await user.save()
    user_dict = jsonable_encoder(user)
    return JSONResponse(
        status_code=200,
        content={
            "msg": 'success',
            "code": '200',
            "data": user_dict
        }
    )


# 编辑个人信息
@app.put("/userInfo/{user_id}")
async def update_user_info(
        user_id: int,
        user_data: dict = Body(...),
):
    user = await User.filter(id=user_id).first()

    if "username" in user_data and user_data["username"] != user.username:
        username = user_data["username"]
        flag = await User.filter(username=username).first()

        if flag:
            raise HTTPException(status_code=404, detail="用户名已存在")

    # 更新用户信息
    for key, value in user_data.items():
        setattr(user, key, value)

    await user.save()

    return JSONResponse(
        status_code=200,
        content={
            "msg": "更新成功！",
            "data": jsonable_encoder(user),
        }
    )


# 获取收藏列表
@app.post("/getFavorites")
async def get_meals_by_ids(list=Body(None)):
    print(list["id_list"])
    id_list = list["id_list"]
    # 获取指定ID的信息
    meals = await Meals.filter(ID__in=id_list).all()
    if meals:
        # 构建返回结果的列表
        meal_list = []
        for meal in meals:
            meal_dict = {
                "Dish_name": meal.Dish_name,
                "Image_URL": meal.Image_URL,
                "ID": meal.ID,
                "Category": meal.Category
            }
            meal_list.append(meal_dict)
        return JSONResponse(
            status_code=200,
            content={
                "status": 200,
                "msg": "获取成功",
                "data": {"meal_list": meal_list,
                         "total": len(meal_list)}
            }
        )

    else:
        return JSONResponse(
            status_code=400,
            content={
                "status": 400,
                "msg": '获取信息失败'
            }
        )

@app.post("/addFavorites")
async def addFavorites(
    user_id: int,
    meal_id: int,
):
    user = await User.filter(id=user_id).first()
    meal = await Meals.filter(ID=meal_id).first()
    id_list = user.favorite
    list = ast.literal_eval(id_list)
    userlist = ast.literal_eval(meal.User_List)
    print(list)
    if meal_id not in list:
        list.append(meal_id)
        user.favorite = list
        userlist.append(user_id)
        meal.User_List = userlist
        meal.fav_num = len(userlist)
        await user.save()
        await meal.save()
        return JSONResponse(
            status_code=200,
            content={
                "msg": "菜谱收藏成功",
                "data": jsonable_encoder(user)
            }
        )
    else:
        while True:
            try:
                list.pop(list.index(meal_id))
            except ValueError:
                break
        while True:
            try:
                userlist.pop(userlist.index(user_id))
            except ValueError:
                break
        user.favorite = list
        meal.User_List = userlist
        meal.fav_num = len(userlist)
        await meal.save()
        await user.save()
        return JSONResponse(
            status_code=200,
            content={
                "msg": "取消收藏成功",
                "data": jsonable_encoder(user)
            }
        )

@app.post("/addView")
async def addView(
    meal_id: int
):
    meal = await Meals.filter(ID=meal_id).first()
    meal.view_num += 1
    await meal.save()
    return JSONResponse(
        status_code=200,
        content={
            "msg": "添加成功",
            "data": jsonable_encoder(meal)
        }
    )
# 定义年龄段划分
AGE_RANGES = [
    (0,  17),
    (18, 25),
    (26, 35),
    (36, 45),
    (46, 100),
]

# 获取用户年龄分布
@app.get("/user/age")
async def get_user_stats():
    users = await User.all()
    
    result = {f"{start}-{end}": 0 for start, end in AGE_RANGES}
    
    for user in users:
        for start, end in AGE_RANGES:
            if user.age is None:  # 如果年龄为空，则跳过该用户
                continue
            if start <= user.age <= end:
                result[f"{start}-{end}"] += 1
                break  # 找到匹配区间后跳出循环
    
    names = list(result.keys())
    values = list(result.values())
    
    return {"msg": "获取成功", "data": {"names": names, "values": values}}


# 获取每月注册量、当前月份登录量
@app.get("/user/monthly_stats")
async def get_user_monthly_stats():
    users = await User.all().order_by("time_sign")
    result_login = {}
    result_sign = {}
    total =await User.all().count()
    for user in users:
        user.time_sign = user.time_sign.strftime("%Y-%m")
        result_sign[f'{user.time_sign}'] = result_sign.get(user.time_sign, 0) + 1
     # 补充注册用户量为0的月份记录
    # 获取最晚的月份
    max_month = datetime.strptime(users[-1].time_sign, "%Y-%m") if isinstance(users[-1].time_sign, str) else users[-1].time_sign.strftime("%Y-%m")

    # 获取过去12个月的月份列表
    twelve_months_ago = (datetime.now() - relativedelta(months=11)).strftime("%Y-%m")
    month_range = [(max_month - relativedelta(months=x)).strftime("%Y-%m") for x in range(12)]

    
    # 遍历过去12个月的月份，如果不在result_sign中，就初始化为0
    for current_month in month_range:
        result_sign.setdefault(current_month, 0)
    sorted_result = dict(sorted(result_sign.items()))
    for user in users:
        user.lastTime = user.lastTime.strftime("%Y-%m")
        if user.lastTime == datetime.now().strftime("%Y-%m"):
            result_login[f'{datetime.now().strftime("%Y-%m")}'] = result_login.get(user.lastTime, 0) + 1
    return JSONResponse(
        status_code=200,
        content={
            "msg": '获取成功',
            "data": {
                "login":{
                    "time": datetime.now().strftime("%Y-%m"),
                    "value":result_login.get(datetime.now().strftime("%Y-%m"),0),
                    "total":total
                },
                "sign":[{"name": key, "value": value} for key, value in sorted_result.items()]
                }
        }
    )

# 获取用户性别比例
@app.get("/user/gender")
async def get_user_gender():
    users = await User.all()
    result = {"male": 0, "female": 0}
    for user in users:
        if user.gender is None:  # 如果性别为空，则跳过该用户
            continue
        if user.gender == 1:
            result["male"] += 1
        elif user.gender == 0:
            result["female"] += 1
    return JSONResponse(
        status_code=200,
        content={
            "msg": '获取成功',
            "data": result
        }
    )

# 分页获取所有用户
@app.get("/user/getAll")
async def get_all_users(page: int = 1, size: int = 10,name: str=None):

    if name is not None:
        users = await User.filter(username__icontains=name).all().offset((page - 1) * size).limit(size)
        total = await User.filter(username__icontains=name).all().count()
    else:
        users = await User.all().offset((page - 1) * size).limit(size)
        total = await User.all().count()
    userList = []
    for user in users:
        userList.append({
            "ID": user.id,
            "username": user.username,
            "age": user.age,
            "gender":user.gender,
            "sign_at": user.time_sign.strftime("%Y-%m-%d %H:%M:%S"),
            "login_at": user.lastTime.strftime("%Y-%m-%d %H:%M:%S"),
            "role":user.role,
            "job":user.job if user.job else "暂无",
            "password":user.password
        })
    return JSONResponse(
        status_code=200,
        content={
            "msg": '获取成功',
            "data": userList,
            "total": total
        }
    )

@app.delete("/deleteUser/{user_id}")
async def delete_user(user_id: int):
    user = await User.filter(id=user_id).first()
    await user.delete()
    return JSONResponse(
        status_code=200,
        content={
            "msg": '删除成功',
        }
    )

@app.put("/recordWeight")
async def record_weight(user_id: int, weight: float, date: str):  # 假设前端传来的date是字符串格式的日期
    user = await User.filter(id=user_id).first()

    # 解析weight_change字段为字典列表
    weight_history = user.weight_change or []

    # 查找是否存在同一天的记录，如果存在则更新，否则添加新记录
    found_and_updated = False
    for i, record_dict in enumerate(weight_history):
        if date in record_dict and isinstance(record_dict[date], (int, float)):  # 确保value是数字类型
            weight_history[i][date] = weight
            found_and_updated = True
            break

    if not found_and_updated:
        weight_history.append({date: weight})

    # 按照日期排序（这里假设日期字符串已经是排序所需的格式）
    weight_history.sort(key=lambda x: list(x.keys())[0])
    # 获取最新记录的体重值并赋值给user的weight字段
    latest_weight = weight_history[-1].get(date, 0)

    user.weight = latest_weight
    # 更新用户体重变化记录
    user.weight_change = weight_history

    await user.save()

    return JSONResponse(
        status_code=200,
        content={
            "code":200,
            "msg": "修改成功",
            "data": jsonable_encoder(user),
        }
    )

class DeleteUsersRequest(BaseModel):
    id_list: List[int]
def filter_weights_by_three_days(data: List[Dict[str, float]]) -> List[Dict[str, float]]:
    """
    过滤每三天一条记录
    """
    filtered_data = []
    last_saved_date = None

    # 按日期逆序排序
    for item in sorted(data, key=lambda x: datetime.strptime(next(iter(x.keys())), '%Y-%m-%d'), reverse=True):
        date = datetime.strptime(next(iter(item.keys())), '%Y-%m-%d')

        if last_saved_date is None or (last_saved_date - date).days >= 3:
            filtered_data.append(item)
            last_saved_date = date

            if len(filtered_data) == 10:
                break

    return  filtered_data[::-1]

@app.get("/weightData/{user_id}")
async def get_user_weight_changes_every_three_days(user_id: int):
    user = await User.filter(id=user_id).first()
    if not user:
        raise HTTPException(status_code=404, detail="User not found")

    weight_history = user.weight_change or []
    # 将weight_change从JSONField转换为列表方便处理
    weight_history_list = [{key: value} for entry in weight_history for key, value in entry.items()]

    # 过滤出每三天一条记录
    every_three_days_records = filter_weights_by_three_days(weight_history_list)

    return JSONResponse(
        status_code=200,
        content={"data": every_three_days_records}
        )

@app.delete("/deleteUsers")
async def delete_meals(list: DeleteUsersRequest):
    if list.id_list:
        deleted_count = await User.filter(id__in=list.id_list).delete()
        return JSONResponse(
            status_code=200,
            content={
                "msg": f"成功删除了{deleted_count}条记录"
            }
        )
    else:
        return JSONResponse(
            status_code=400,
            content={
                "msg": "请传入id_list"
            }
        )
#  -------   食谱接口    ----------
# 获取一定数量的食谱
@app.get("/getMeals/{num}")
async def getMeal(num: int):
    meals = await Meals.all().limit(num)
    meal_list = []
    for meal in meals:
        meal_dict = {
            "Dish_name": meal.Dish_name,
            "image": meal.Image_URL,
            "id": meal.ID,
            "category": meal.Category
        }
        meal_list.append(meal_dict)
    return JSONResponse(
        content={
            "code": 200,
            "msg": "获取成功",
            "data": meal_list
        }
    )


# 名称搜索食谱
@app.get("/searchMeals")
async def search_meal(
    name: str=None,
    current_page: int = Query(1, gt=0, description="当前页码"),
    page_size: int = Query(8, gt=0, le=100, description="每页显示的项数"),
    sort_field: str = "add_time", 
    sort_direction: str = "desc",
    type:str =None
):
    
    meals,total = await paginate_meals(current_page,page_size,sort_field,sort_direction,name,type)
    meal_list = []
    
    for meal in meals:
        meal_dict = {
            "name": meal.Dish_name,
            "image": meal.Image_URL,
            "id": meal.ID,
            "category": meal.Category,
            "type":meal.meal_type_y,
            "cookTime":meal.Cooking_Time,
        }
        meal_list.append(meal_dict) 
    if meals:
        existing_keyword = await Keyword.get_or_none(keyword=name)
        if existing_keyword:
            # 如果关键词已存在，更新计数
            existing_keyword.count += 1
            await existing_keyword.save()
        else:
            # 如果关键词不存在，创建新的关键词记录
            new_keyword = Keyword(keyword=name, count=1)
            await new_keyword.save()
        return JSONResponse(
            content={
                'msg': '搜索成功',
                'code': '200',
                'data': meal_list,
                'total': total
            },

        )
    else:
        raise HTTPException(status_code=404, detail="Meal not found")


# 热搜关键字
@app.get("/hot_search")
async def hot_search():
    # 获取热搜排名前6的关键词
    hot_keywords = await Keyword.all().order_by("-count").limit(6)
    items = []
    for item in hot_keywords:
        item={
            "keyword":item.keyword,
            "count":item.count
        }
        items.append(item)
    return JSONResponse(
        status_code=200,
        content={
            "data":items
        }
    )
# 热搜食谱
@app.get("/hotMeals")
async def top_meals():
    # 获取热搜前五个关键词
    top_keywords = await Keyword.all().order_by("-count").limit(5)

    # 创建一个集合来存储已经查询过的食谱的 ID
    queried_meal_ids: Set[int] = set()

    # 通过关键词查询对应的食谱
    meals = []
    for keyword in top_keywords:
        meal_query = await Meals.filter(Dish_name__icontains=keyword.keyword).limit(2)
        meals.extend(meal_query)

    # 对查询到的食谱进行去重
    unique_meals = []
    for meal in meals:
        if meal.ID not in queried_meal_ids:
            unique_meals.append(meal)
            queried_meal_ids.add(meal.ID)

    # 构造食谱的 JSON 响应
    meal_list = []
    for meal in unique_meals:
        meal_dict = {
            "Dish_name": meal.Dish_name,
            "image": meal.Image_URL,
            "id": meal.ID,
            "category": meal.Category,
            "meal_type": meal.meal_type_y
        }
        meal_list.append(meal_dict)
        if len(meal_list) == 5:
            break

    return JSONResponse(
        status_code=200,
        content={
            "data": meal_list
        }
    )
# 获取一日三餐
@app.get("/getThreeMeal")
async def getMeal(num=12):
    all_meals = await Meals.all()

    # Separate meals by type
    breakfast_meals = [meal for meal in all_meals if meal.meal_type_y == 'breakfast']
    lunch_meals = [meal for meal in all_meals if meal.meal_type_y == 'lunch']
    dinner_meals = [meal for meal in all_meals if meal.meal_type_y == 'dinner']
    # Randomly select 4 meals of each type
    selected_meals = []
    shuffle(breakfast_meals)
    shuffle(lunch_meals)
    shuffle(dinner_meals)

    selected_meals.extend(breakfast_meals[:4])
    selected_meals.extend(lunch_meals[:4])
    selected_meals.extend(dinner_meals[:4])

    meal_list = []
    for meal in selected_meals[:num]:
        meal_dict = {
            "Dish_name": meal.Dish_name,
            "image": meal.Image_URL,
            "id": meal.ID,
            "category": meal.Category,
            "meal_type": meal.meal_type_y
        }
        meal_list.append(meal_dict)

    return JSONResponse(
        content={
            "code": 200,
            "msg": "获取成功",
            "data": meal_list
        }
    )


# 获取最新上传的食谱
@app.get("/newMeals")
async def get_latest_recipe():
    meals = await Meals.filter().order_by('-add_time').limit(3)
    print(meals)
    if not meals:
        raise HTTPException(status_code=404, detail="No recipes found")
    meal_list = []
    for meal in meals:
        meal_dict = {
            "Dish_name": meal.Dish_name,
            "image": meal.Image_URL,
            "id": meal.ID,
            "category": meal.Category,
            "meal_type": meal.meal_type_y
        }
        meal_list.append(meal_dict)
    return JSONResponse(
        status_code=200,
        content={
            "msg": '获取成功',
            "data": meal_list
        }
    )


# 获取收藏排名
@app.get("/topFavorite")
async def get_top_three_recipes():
    meals = await Meals.all().order_by("-fav_num").limit(3)
    if not meals:
        raise HTTPException(status_code=404, detail="No recipes found")

    meal_list = []
    for meal in meals:
        meal_dict = {
            "Dish_name": meal.Dish_name,
            "image": meal.Image_URL,
            "id": meal.ID,
            "category": meal.Category,
            "fav_num":meal.fav_num
        }
        meal_list.append(meal_dict)
    return JSONResponse(
        status_code=200,
        content={
            "msg": '获取成功',
            "data": meal_list
        }
    )

@app.get("/getMeal/{id}")
async def get_meal(id: int):
    meal = await Meals.get(ID=id)
    if meal is None:
        raise HTTPException(status_code=404, detail="Meal not found")
    meal_dict ={
        "Dish_name": meal.Dish_name,
        "image": meal.Image_URL,
        "id": meal.ID,
        "category": meal.Category,
        "meal_type": meal.meal_type_y,
        "Calories": meal.Calories,
        "Carbohydrates_y": meal.Carbohydrates_y,
        "Fat_y": meal.Fat_y,
        "Protein_y": meal.Protein_y,
        "Fiber_y": meal.Fiber_y,
        "Ingredients": meal.Ingredients,
        "Main_Technique": meal.Main_Technique,
        "Method": meal.Method,
        "Taste": meal.Taste,
        "Cooking_Time": meal.Cooking_Time,
        "fav_num":meal.fav_num,
        "view_num":meal.view_num
    }
    return JSONResponse(
        status_code=200,
        content={
            "msg": '获取成功',
            "data": meal_dict
        }
    )

def calculate_cosine_similarity(vector1, vector2):
    text1 = ' '.join(vector1)
    text2 = ' '.join(vector2)
    vectorizer = CountVectorizer().fit_transform([text1, text2])
    cosine_sim = cosine_similarity(vectorizer)
    return cosine_sim[0][1]

async def get_meals_by_similarity(dish_name, method, taste, original_meal_id=None, threshold=0.2):
    # 获取所有除原始菜肴之外的数据
    if original_meal_id:
        all_meals = await Meals.exclude(ID=original_meal_id).all()
    else:
        all_meals = await Meals.all()

    # 初始化一个空列表存储相似菜肴及其相似度
    similar_meals = []

    for meal in all_meals:
        vector = [meal.Dish_name, meal.Method, meal.Taste]
        similarity = calculate_cosine_similarity([dish_name, method, taste], vector)
        
        if similarity >= threshold:
            similar_meals.append({"meal_id": meal.ID,"image": meal.Image_URL,"name":meal.Dish_name, "similarity": similarity})

    # 按相似度降序排序
    similar_meals = sorted(similar_meals, key=lambda x: x["similarity"], reverse=True)[:4]
    dish = await Meals.filter(ID=original_meal_id).first()
    while len(similar_meals) < 4:
        random_meal = random.choice(all_meals)
        if random_meal.meal_type_y == dish.meal_type_y:  # 假设菜肴类型存储在 meal.Type 中
            similar_meals.append({"meal_id": random_meal.ID,"image": random_meal.Image_URL,"name": random_meal.Dish_name, "similarity": 0.0})  # 相似度设为0，表示随机添加的菜肴

    return similar_meals[:4]  # 最多返回四个相似菜肴



@app.get("/recommendSimilar")
async def recommend(dish_name: str, method: str, taste: str,id:str):
    similar_meals = await get_meals_by_similarity(dish_name, method, taste,id)
    
    if not similar_meals:
        raise HTTPException(status_code=404, detail="No similar meals found")
    return JSONResponse(
        status_code=200,
        content={
            "msg": '获取成功',
            "data": [{'id': meal['meal_id'], 'image': meal['image'], 'name': meal['name'],"similarity":meal['similarity']} for meal in similar_meals]
        }
    )

async def paginate_meals(
    page: int = 1,
    per_page: int = 10,
    sort_field: str = "add_time",
    sort_direction: str = "desc",
    name:str = None,
    type:str = None
) -> QuerySet["Meals"]:
    if sort_direction == "asc":
        order_by = f"{sort_field}"
    elif sort_direction == "desc":
        order_by = f"-{sort_field}"
    else:
        raise HTTPException(status_code=400, detail="Invalid sort direction")
    if name != None and type != "all":
        meals_query = await Meals.filter(Dish_name__icontains=name).filter(meal_type_y=type).all().order_by(order_by).offset((page - 1) * per_page).limit(per_page)
        total = await Meals.filter(Dish_name__icontains=name).filter(meal_type_y=type).all().count()
    elif name != None and type == "all":
        meals_query = await Meals.filter(Dish_name__icontains=name).all().order_by(order_by).offset((page - 1) * per_page).limit(per_page)
        total = await Meals.filter(Dish_name__icontains=name).all().count()
    elif name == None and type != "all":
        meals_query = await Meals.filter(meal_type_y=type).all().order_by(order_by).offset((page - 1) * per_page).limit(per_page)
        total = await Meals.filter(meal_type_y=type).all().count()
    else:
        meals_query = await Meals.all().order_by(order_by).offset((page - 1) * per_page).limit(per_page)
        total = await Meals.all().count()
    return meals_query,total

@app.get("/meals")
async def get_meals(page: int = 1, per_page: int = 10, sort_field: str = "add_time", sort_direction: str = "desc",name:str=None,type:str=None):
    meals,total = await paginate_meals(page, per_page, sort_field, sort_direction,name,type)
    print(meals)
    meal_list = []
    for meal in meals:
        meal_dict = {
            "name": meal.Dish_name,
            "image": meal.Image_URL,
            "ID": meal.ID,
            "category": meal.Category,
            "type": meal.meal_type_y,
            "taste": meal.Taste,
            "favNum":meal.fav_num,
            "viewNum":meal.view_num,
            "date":meal.add_time.strftime("%Y-%m-%d %H:%M:%S"),
        }
            
        meal_list.append(meal_dict)
        
    return JSONResponse(
        status_code=200,
        content={
        "msg": '获取成功',
        "data": meal_list,
        "total": total
        }
    )
@app.get("/top_ten_recipes")
async def get_top_ten_recipes():
    meals = await Meals.all().order_by("-view_num").limit(10)
    if not meals:
        raise HTTPException(status_code=404, detail="No recipes found")

    meal_names = [meal.Dish_name for meal in meals]
    view_nums = [meal.view_num for meal in meals]
    return JSONResponse(
        status_code=200,
        content={
            "msg": '获取成功',
            "data": {
                "meal_names": meal_names,
                "view_nums": view_nums
            }
        }
    )
ENGLISH_TO_CHINESE = {
    "breakfast": "早餐",
    "lunch": "午餐",
    "dinner": "晚餐"
}
@app.get("/meal_type") 
async def get_meal_type_distribution():
     
    types = await Meals.all().values_list('meal_type_y', flat=True)
    
    # 处理查询结果
    type_count = {}
    for type_ in types:
        type_count[type_] = type_count.get(type_, 0) + 1
    
    # 将结果转换为期望的格式，并将英文餐次名称转换为中文
    result = []
    for type_, count in type_count.items():
        chinese_type = ENGLISH_TO_CHINESE.get(type_, type_)  # 如果找不到对应中文，则保留英文
        result.append({"value": count, "name": chinese_type})
     
    return JSONResponse(
        status_code=200,
        content={
            "msg": '获取成功',
            "data": result
        }
    )

@app.get("/top_fav_num")
async def get_top_fav_num():
    meals = await Meals.all().order_by('-fav_num').limit(10)
    
    newmeals =list(reversed(meals))
    # 将结果转换为期望的格式
    result = []
    for meal in newmeals:
        result.append({"value": meal.fav_num, "name": meal.Dish_name})
    
    return JSONResponse(
        status_code=200,
        content={
            "msg": '获取成功',
            "data": result
        }
    )

@app.delete("/deleteMeal/{id}")
async def delete_meal(id: str):
    meal = await Meals.filter(ID=id).first()
    if not meal:
        raise HTTPException(status_code=404, detail="Meal not found")
    await meal.delete()
    return JSONResponse(
        status_code=200,
        content={
            "msg": '删除成功',
            "data": id
        }
    )

class DeleteMealsRequest(BaseModel):
    id_list: List[int]

@app.delete("/deleteMeals")
async def delete_meals(list: DeleteMealsRequest):
    if list.id_list:
        deleted_count = await Meals.filter(ID__in=list.id_list).delete()
        return JSONResponse(
            status_code=200,
            content={
                "msg": f"成功删除了{deleted_count}条记录"
            }
        )
    else:
        return JSONResponse(
            status_code=400,
            content={
                "msg": "请传入id_list"
            }
        )
       
class MealIn(BaseModel):
    name: str
    image: str
    category: str
    type: str
    taste: str
    ingredients: str
    method: str
    Protein: float
    Carbohydrates: float
    Fat: float
    Fiber: float
    Calories: str
    Main_Technique: str
    Cooking_Time: str


@app.post("/addMeal")
async def add_meal(mealin: MealIn = Body(...)):
    meal = mealin.dict()
    # img = meal["image"]
    # meal["image"] = base64.b64encode(img).decode('utf-8')
    await Meals.create(
        Dish_name=meal["name"],
        Image_URL=meal["image"],
        Category=meal["category"],
        meal_type_y=meal["type"],
        Taste=meal["taste"],
        Ingredients=meal["ingredients"],
        Method=meal["method"],
        Fav_num=0,
        View_num=0,
        add_time=datetime.now(),
        User_List="[]",
        Protein_y=meal["Protein"],
        Carbohydrates_y=meal["Carbohydrates"],
        Fat_y=meal["Fat"],
        Fiber_y=meal["Fiber"],
        Calories=meal["Calories"],
        Main_Technique=meal["Main_Technique"],
        Cooking_Time=meal["Cooking_Time"],
    )
    return JSONResponse(
        status_code=200,
        content={
            "msg": '添加成功',
            "data": meal
        }
    )

activity_mapping = {
    '教师': 1.10,
    '记者': 1.15,
    '演员': 1.15,
    '厨师': 1.15,
    '医生': 1.10,
    '护士': 1.20,
    '司机': 1.10,
    '军人': 1.20,
    '律师': 1.10,
    '商人': 1.10,
    '会计': 1.10,
    '程序员': 1.10,
    '服务员': 1.20,
    '作家': 1.10,
    '模特': 1.13,
    '导游': 1.20,
    '歌手': 1.12,
    '裁缝': 1.10,
    '保安': 1.10,
    '保洁': 1.10,
    '花匠': 1.11,
    '清洁工': 1.14,
    '理发师': 1.10,
    '消防员': 1.20,
    '推销员': 1.20,
    '运动员': 1.30,
    '快递员': 1.20,
    '外卖员': 1.15,
    '主持人': 1.12,
    '漫画家': 1.10,
    '面点师': 1.11,
    '美甲师': 1.10,
    '保姆': 1.12,
    '电力工程师': 1.11,
    '化验员': 1.10,
    '机长': 1.10,
    '空姐': 1.12,
    '画师': 1.10,
    '动漫设计': 1.10,
    '白领': 1.10,
    '前台': 1.10,
    '学生': 1.10,
}

def calculate_bmr(weight: float, height: float, age: int, gender: int) -> float:
    if gender == 1:
        bmr = 88.36 + (13.397 * weight) + (4.799 * height) - (5.677 * age)
    else:
        bmr = 447.6 + (9.247 * weight) + (3.098 * height) - (4.330 * age)
    return bmr


def calculate_tdee(bmr: float, activity_multiplier: float) -> float:
    tdee = bmr * activity_multiplier
    return tdee


def calculate_macros(tdee: float, protein_factor: float = 0.15, fiber_factor: float = 0.01) -> tuple:
    calories = tdee
    protein = protein_factor * tdee
    fat = 0.25 * tdee
    carbohydrates = 0.55 * tdee
    fiber = fiber_factor * tdee
    return calories, carbohydrates, fat, protein, fiber

@app.post("/recommendMeals/{user_id}")
async def recommend_meals(user_id: int):
    userInfo = await User.filter(id=user_id).first()
    bmr = calculate_bmr(userInfo.weight, userInfo.height, userInfo.age, userInfo.gender)
    if userInfo.job not in activity_mapping:
        activity_multiplier = 1.2
    else:
        activity_multiplier = activity_mapping[userInfo.job]
    tdee = calculate_tdee(bmr, activity_multiplier)
    user_need = calculate_macros(tdee)
    meals = await Meals.all()
    # 提取食谱营养素信息
    meal_nutrients = [[meal.Calories, meal.Carbohydrates_y, meal.Fat_y, meal.Protein_y, meal.Fiber_y] for meal in meals]
    # 数据标准化
    scaler = StandardScaler()
    meal_nutrients_scaled = scaler.fit_transform(meal_nutrients)
    # 聚类
    kmeans = KMeans(n_clusters=12)
    kmeans.fit(meal_nutrients_scaled)
    # 找到用户需求所属的聚类
    user_need_scaled = scaler.transform([user_need])
    # 计算用户需求与所有食谱的距离
    distances = pairwise_distances(user_need_scaled, meal_nutrients_scaled)
    closest_meal_indices = distances.argsort()[0]
    breakfast = []
    lunch = []
    dinner = []
    for index in closest_meal_indices:
        recommended_meal = meals[index]
        if recommended_meal.meal_type_y == 'breakfast' and len(breakfast) < 4:
            breakfast.append(recommended_meal)
        elif recommended_meal.meal_type_y == 'lunch' and len(lunch) < 4:
            lunch.append(recommended_meal)
        elif recommended_meal.meal_type_y == 'dinner' and len(dinner) < 4:
            dinner.append(recommended_meal)
    # recommended_meals = [meals[i] for i in closest_meal_indices]
    res = breakfast + lunch + dinner
    meal_list = []
    for meal in res:
        meal_dict = {
            "Dish_name": meal.Dish_name,
            "image": meal.Image_URL,
            "id": meal.ID,
            "category": meal.Category,
            "meal_type": meal.meal_type_y
        }
        meal_list.append(meal_dict)

    return JSONResponse(
        content={
            "code": 200,
            "msg": "获取成功",
            "data": meal_list
        }
    )
   